﻿namespace SpikingAnalyticsFrameLib

open SpikingAnalyticsLib
open SpikingAnalyticsLib.ResponseFingerPrint
open Deedle

[<AbstractClass; Sealed>]
type WindowMapComparisonFrame () =

    /// Compare mean-variance data of two WindowMaps
    /// Output frame format: ["nindex"; "meanReference"; "varianceReference"; "meanComparison"; "varianceComparison";]
    static member GetMeanVarianceData(referenceWindowMap, comparisonWindowMap) =
        WindowMap.GetMeanVarianceData(referenceWindowMap, comparisonWindowMap)
        |> Seq.map (fun (nindex, (mean1, variance1, windowSpikes1, totalSpikes1), (mean2, variance2, windowSpikes2, totalSpikes2)) ->
                // NO meta, then WITH meta
                nindex, mean1, variance1, mean2, variance2
            )
        |> Frame.ofRecords
        |> Frame.indexColsWith(["nindex"; "meanReference"; "varianceReference"; "meanComparison"; "varianceComparison";])

    /// Extract the PNG structure from the comparison frame using the specified network state data
    /// Input frame format: ["nindex"; "meanReference"; "varianceReference"; "meanComparison"; "varianceComparison";]
    /// Output frame format: ["t1"; "n1"; "t2"; "n2"; "weight"; "probability";]
    static member GetPNGStructure(comparisonFrame, networkPath) =
        let GetFiringEvents (comparisonFrame:Frame<int, string>) =
            Series.observations comparisonFrame.Rows
            |> Seq.map (fun (key, row) ->
                    // Create points (x, y) from (mean peak value, nindex)
                    int (round (row.GetAs<float> "meanComparison")), (row.GetAs<int> "nindex"), 0.0
                )
            |> Seq.toList

        let network = CrossbarNetwork.CreateFromFile(networkPath)
        let firingEvents = GetFiringEvents comparisonFrame
        let descriptor = network.GetPNGDescriptor(firingEvents, 0.0)

        // transform the data into the required form
        descriptor.LinkedEventsAsTuples
        |> Seq.map (fun ((preTime, preNeuron), (postTime, postNeuron), connectionWeight, probability) ->
                // unwrap each LinkedEventPair
                preTime, preNeuron, postTime, postNeuron, connectionWeight, probability
            )
        |> Frame.ofRecords
        |> Frame.indexColsWith(["t1"; "n1"; "t2"; "n2"; "weight"; "probability";])

    /// Extract firing events using firing times from the comparison peak means
    /// Color-code the comparison firing events relative to the reference as being within narrowing (blue) or widening (red) peaks
    /// Input frame format: ["nindex"; "meanReference"; "varianceReference"; "meanComparison"; "varianceComparison";]
    /// Output frame format: ["time"; "nindex"; "color";]
    static member ClassifyFiringEvents(comparisonFrame:Frame<int, string>) =
        Series.observations comparisonFrame.Rows
        |> Seq.map (fun (key, row) ->
                let peakMean, nindex = int (round (row.GetAs<float> "meanComparison")), (row.GetAs<int> "nindex")
                let varianceReference, varianceComparison = (row.GetAs<float> "varianceReference"), (row.GetAs<float> "varianceComparison")
                let pointColour = if (varianceReference > varianceComparison) then "blue" else "red"
                peakMean, nindex, pointColour
            )
        |> Frame.ofRecords
        |> Frame.indexColsWith(["time"; "nindex"; "color";])
